"""
市场情绪分析模块

分析市场整体情绪，包括：
- 涨停家数统计
- 连板高度
- 炸板率
- 市场温度计

作者: AI Assistant  
版本: 1.0.0
日期: 2024-10-10
"""

import pandas as pd
import numpy as np
from datetime import datetime
import logging
from typing import Dict, Optional

logger = logging.getLogger(__name__)


class MarketSentiment:
    """市场情绪分析器"""
    
    @staticmethod
    def analyze_sentiment(limit_up_df: pd.DataFrame) -> Dict:
        """
        分析市场情绪
        
        参数:
            limit_up_df: 涨停板数据DataFrame
            
        返回:
            Dict: 市场情绪指标
        """
        try:
            if limit_up_df is None or limit_up_df.empty:
                return {
                    'total_limit_up': 0,
                    'max_consecutive': 0,
                    'sentiment_score': 0,
                    'sentiment_level': '极冷',
                    'market_status': '冰点',
                    'avg_seal_strength': 0,
                    'early_board_ratio': 0,
                    'strong_board_ratio': 0
                }
            
            # 1. 涨停家数
            total_limit_up = len(limit_up_df)
            
            # 2. 最高连板数
            if 'consecutive_limit_up' in limit_up_df.columns:
                max_consecutive = int(limit_up_df['consecutive_limit_up'].max())
            else:
                max_consecutive = 0
            
            # 3. 平均封板强度
            if 'seal_amount' in limit_up_df.columns and 'market_cap' in limit_up_df.columns:
                seal_strengths = []
                for _, row in limit_up_df.iterrows():
                    seal = float(row.get('seal_amount', 0)) if pd.notna(row.get('seal_amount')) else 0
                    cap = float(row.get('market_cap', 1)) if pd.notna(row.get('market_cap')) else 1
                    if cap > 0:
                        seal_strengths.append((seal / cap) * 100)
                avg_seal_strength = np.mean(seal_strengths) if seal_strengths else 0
            else:
                avg_seal_strength = 0
            
            # 4. 早盘板占比
            if 'first_limit_up_time' in limit_up_df.columns:
                early_boards = 0
                for time_str in limit_up_df['first_limit_up_time']:
                    if pd.notna(time_str):
                        try:
                            time_parts = str(time_str).split(':')
                            if len(time_parts) >= 2:
                                hour = int(time_parts[0])
                                minute = int(time_parts[1])
                                if hour * 60 + minute < 600:  # 10:00之前
                                    early_boards += 1
                        except:
                            pass
                early_board_ratio = (early_boards / total_limit_up * 100) if total_limit_up > 0 else 0
            else:
                early_board_ratio = 0
            
            # 5. 强势板占比（封板强度>3%）
            if 'seal_amount' in limit_up_df.columns and 'market_cap' in limit_up_df.columns:
                strong_boards = 0
                for _, row in limit_up_df.iterrows():
                    seal = float(row.get('seal_amount', 0)) if pd.notna(row.get('seal_amount')) else 0
                    cap = float(row.get('market_cap', 1)) if pd.notna(row.get('market_cap')) else 1
                    if cap > 0 and (seal / cap) * 100 > 3:
                        strong_boards += 1
                strong_board_ratio = (strong_boards / total_limit_up * 100) if total_limit_up > 0 else 0
            else:
                strong_board_ratio = 0
            
            # 6. 计算情绪分数（0-100）
            sentiment_score = 0
            
            # 涨停家数评分（最高40分）
            if total_limit_up >= 100:
                sentiment_score += 40
            elif total_limit_up >= 60:
                sentiment_score += 30
            elif total_limit_up >= 30:
                sentiment_score += 20
            elif total_limit_up >= 10:
                sentiment_score += 10
            else:
                sentiment_score += 5
            
            # 连板高度评分（最高30分）
            if max_consecutive >= 5:
                sentiment_score += 30
            elif max_consecutive >= 3:
                sentiment_score += 20
            elif max_consecutive >= 2:
                sentiment_score += 10
            else:
                sentiment_score += 5
            
            # 早盘板占比评分（最高15分）
            if early_board_ratio >= 50:
                sentiment_score += 15
            elif early_board_ratio >= 30:
                sentiment_score += 10
            else:
                sentiment_score += 5
            
            # 强势板占比评分（最高15分）
            if strong_board_ratio >= 50:
                sentiment_score += 15
            elif strong_board_ratio >= 30:
                sentiment_score += 10
            else:
                sentiment_score += 5
            
            # 7. 情绪等级
            if sentiment_score >= 80:
                sentiment_level = '极热'
                market_status = '高潮'
            elif sentiment_score >= 60:
                sentiment_level = '火热'
                market_status = '活跃'
            elif sentiment_score >= 40:
                sentiment_level = '温和'
                market_status = '正常'
            elif sentiment_score >= 20:
                sentiment_level = '偏冷'
                market_status = '低迷'
            else:
                sentiment_level = '极冷'
                market_status = '冰点'
            
            return {
                'total_limit_up': total_limit_up,
                'max_consecutive': max_consecutive,
                'sentiment_score': sentiment_score,
                'sentiment_level': sentiment_level,
                'market_status': market_status,
                'avg_seal_strength': round(avg_seal_strength, 2),
                'early_board_ratio': round(early_board_ratio, 2),
                'strong_board_ratio': round(strong_board_ratio, 2),
                'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            }
            
        except Exception as e:
            logger.error(f"分析市场情绪失败: {e}")
            return {
                'total_limit_up': 0,
                'max_consecutive': 0,
                'sentiment_score': 0,
                'sentiment_level': '未知',
                'market_status': '未知',
                'avg_seal_strength': 0,
                'early_board_ratio': 0,
                'strong_board_ratio': 0
            }
    
    @staticmethod
    def get_industry_distribution(limit_up_df: pd.DataFrame) -> pd.DataFrame:
        """
        获取涨停板块分布
        
        参数:
            limit_up_df: 涨停板数据
            
        返回:
            DataFrame: 板块分布统计
        """
        try:
            if limit_up_df is None or limit_up_df.empty or 'industry' not in limit_up_df.columns:
                return pd.DataFrame()
            
            industry_counts = limit_up_df['industry'].value_counts().reset_index()
            industry_counts.columns = ['行业', '涨停数']
            industry_counts['占比(%)'] = (industry_counts['涨停数'] / len(limit_up_df) * 100).round(2)
            
            return industry_counts.head(10)  # 返回前10个行业
            
        except Exception as e:
            logger.error(f"统计板块分布失败: {e}")
            return pd.DataFrame()
    
    @staticmethod
    def calculate_explosive_rate(limit_up_df: pd.DataFrame, minute_data: Dict) -> float:
        """
        计算炸板率
        
        参数:
            limit_up_df: 涨停板数据
            minute_data: 分时数据字典（股票代码: 分时DataFrame）
            
        返回:
            float: 炸板率百分比
        """
        try:
            if limit_up_df is None or limit_up_df.empty:
                return 0.0
            
            explosive_count = 0
            total_count = len(limit_up_df)
            
            for _, row in limit_up_df.iterrows():
                code = row.get('code')
                first_limit_time = row.get('first_limit_up_time')
                last_limit_time = row.get('last_limit_up_time')
                
                # 如果首次和最后封板时间不同，说明有开板
                if pd.notna(first_limit_time) and pd.notna(last_limit_time):
                    if first_limit_time != last_limit_time:
                        explosive_count += 1
            
            explosive_rate = (explosive_count / total_count * 100) if total_count > 0 else 0
            return round(explosive_rate, 2)
            
        except Exception as e:
            logger.error(f"计算炸板率失败: {e}")
            return 0.0
    
    @staticmethod
    def get_sentiment_advice(sentiment: Dict) -> str:
        """
        根据市场情绪给出建议
        
        参数:
            sentiment: 情绪分析结果
            
        返回:
            str: 操作建议
        """
        score = sentiment.get('sentiment_score', 0)
        level = sentiment.get('sentiment_level', '未知')
        
        if score >= 80:
            return """
🔥 市场极度火热，情绪高涨！
⚠️ 注意事项：
- 极度活跃往往意味着风险积累
- 谨防追高被套，注意获利了结
- 建议减少仓位，保持警惕
- 高位情绪容易瞬间反转
            """
        elif score >= 60:
            return """
🌟 市场活跃，氛围较好
✅ 操作建议：
- 可适当参与短线机会
- 优先选择龙头和强势板块
- 控制仓位，设置止损
- 注意板块轮动节奏
            """
        elif score >= 40:
            return """
📊 市场情绪正常，机会一般
💡 操作建议：
- 观望为主，精选个股
- 优先关注连板股和龙头
- 严格执行纪律，不追弱势板
- 可小仓位试错
            """
        elif score >= 20:
            return """
❄️ 市场偏冷，赚钱效应差
⚠️ 操作建议：
- 谨慎参与，以观望为主
- 避免盲目抄底
- 等待市场情绪回暖信号
- 保留现金，耐心等待
            """
        else:
            return """
🧊 市场极冷，情绪冰点
🛑 操作建议：
- 强烈建议空仓观望
- 不要轻易抄底
- 等待明确的转强信号
- 保护本金最重要
            """

